package com.haoxuer.discover.user.rest.resource;

import com.haoxuer.discover.data.enums.StoreState;
import com.haoxuer.discover.filter.base.HandlerFilterProxy;
import com.haoxuer.discover.filter.base.MockFilterChain;
import com.haoxuer.discover.filter.base.RestRequest;
import com.haoxuer.discover.filter.common.Filter;
import com.haoxuer.discover.filter.common.Handler;
import com.haoxuer.discover.filter.common.HandlerResponse;
import com.haoxuer.discover.plug.data.service.CodeService;
import com.haoxuer.discover.rest.base.ResponseObject;
import com.haoxuer.discover.user.api.apis.UserHandler;
import com.haoxuer.discover.user.api.domain.request.*;
import com.haoxuer.discover.user.api.domain.response.UserResponse;
import com.haoxuer.discover.user.data.dao.*;
import com.haoxuer.discover.user.data.entity.*;
import com.haoxuer.discover.user.data.enums.BindType;
import com.haoxuer.discover.user.data.enums.SecurityType;
import com.haoxuer.discover.user.data.request.UpdatePasswordRequest;
import com.haoxuer.discover.user.oauth.api.OauthHandler;
import com.haoxuer.discover.user.oauth.domain.OauthResponse;
import com.haoxuer.discover.user.oauth.domain.TokenResponse;
import com.haoxuer.discover.user.rest.adapter.ResponseAdapter;
import com.haoxuer.discover.user.rest.filters.CheckUserTokenFilter;
import com.haoxuer.discover.user.rest.filters.ExtractOauthFilter;
import com.haoxuer.discover.user.rest.vo.SendCodeVo;
import com.haoxuer.discover.user.service.UserTokenService;
import com.haoxuer.discover.user.utils.CodeCatalog;
import com.haoxuer.discover.user.utils.SecurityUtil;
import com.haoxuer.discover.user.word.AdaptiveRandomWordFactory;
import com.vdurmont.emoji.EmojiParser;
import jodd.util.StringUtil;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.Calendar;
import java.util.Date;
import java.util.List;

/**
 * Created by ada on 2017/6/29.
 */

@Scope("prototype")
@Transactional
@Component
public class UserResource implements UserHandler, ApplicationContextAware {

  private ApplicationContext context;

  @Autowired
  private UserVerificationDao verificationDao;

  @Autowired
  private UserAccountDao accountDao;

  @Autowired
  private UserInfoDao userInfoDao;

  @Autowired
  private UserOauthConfigDao configDao;

  @Autowired
  private CodeService codeService;


  @Autowired
  private UserOauthTokenDao oauthTokenDao;

  @Autowired
  private UserTokenService tokenService;

  @Autowired
  private UserBindDao bindDao;

  @Autowired
  private UserSecurityDao securityDao;

  public SendCodeVo validate(Date lastDate) {
    SendCodeVo result = new SendCodeVo();
    Long time = checkDate(lastDate);
    if (time < 5 * 60 * 1000) {
      result.setCode(-1);
    }
    return result;
  }

  public SendCodeVo check(Date lastDate) {
    SendCodeVo result = new SendCodeVo();
    Long time = checkDate(lastDate);
    if (time > 5 * 60 * 1000) {
      result.setCode(-1);
    }
    return result;
  }

  private Long checkDate(Date lastDate) {
    if (lastDate == null) {
      Calendar calendar = Calendar.getInstance();
      lastDate = calendar.getTime();
    }
    return System.currentTimeMillis() - lastDate.getTime();
  }

  public SendCodeVo send(String template, String phone) {
    SendCodeVo result = new SendCodeVo();

    AdaptiveRandomWordFactory f = new AdaptiveRandomWordFactory();
    f.setMinLength(4);
    f.setMaxLength(4);
    f.setCharacters("1234567890");
    String vcode = "" + f.getNextWord();
    result.setSendCode(vcode);
    boolean res = codeService.sendCode(vcode, template, phone);
    if (!res) {
      result.setCode(-1);
    }
    return result;
  }


  @Override
  public ResponseObject sendCode(SendCodeRequest request) {
    ResponseObject result = new ResponseObject();

    Integer catalog = CodeCatalog.catalog(request.getCatalog());
    if (catalog == null) {
      result.setCode(-1);
      result.setMsg("参数异常");
      return result;
    }
    if (request.getCatalog().equals("register")
      || request.getCatalog().equals("changePhone")) {
      UserBind bind = bindDao.findByPhone(request.getPhone());
      if (bind != null) {
        result.setCode(-2);
        result.setMsg("该手机号已经注册过了");
        return result;
      }
    }
    if ("reset".equals(request.getCatalog())) {
      if (bindDao.findByPhone(request.getPhone()) == null) {
        result.setCode(-3);
        result.setMsg("该手机号未注册！");
        return result;
      }
    }

    UserVerification verification = verificationDao.findByName(request.getPhone(), catalog);
    if (verification != null) {
      Date lastDate = verification.getLastDate();
      SendCodeVo validateCode = validate(lastDate);
      if (validateCode.getCode() != 0) {
        result.setCode(-3);
        result.setMsg("验证码还没有过期");
        return result;
      }
      SendCodeVo send = send(request.getCatalog(), request.getPhone());
      if (send.getCode() != 0) {
        result.setCode(-1);
        result.setMsg("发送验证码失败");
        return result;
      }
      verification.setName(request.getPhone());
      verification.setCode(send.getSendCode());
      verification.setLastDate(new Date());
      verificationDao.update(verification);
    } else {
      UserVerification v = new UserVerification();
      SendCodeVo send = send(request.getCatalog(), request.getPhone());
      if (send.getCode() != 0) {
        result.setCode(-1);
        result.setMsg("发送验证码失败");
        return result;
      }
      v.setName(request.getPhone());
      v.setCode(send.getSendCode());
      v.setCatalog(catalog);
      verificationDao.save(v);
    }
    return result;
  }

  @Override
  public UserResponse loginByCode(UserLoginCodeRequest request) {
    UserResponse result = new UserResponse();


    if (checkCode(request.getPhone(), request.getCode(), 2, result)) {
      return result;
    }

    UserInfo user = null;

    UserBind bind = bindDao.findByName(request.getPhone());
    if (bind == null) {
      user = new UserInfo();
      user.setPhone(request.getPhone());
      userInfoDao.save(user);
      bind = new UserBind();
      bind.setBindType(BindType.phone);
      bind.setNo(request.getPhone());
      bind.setUser(user);
      bindDao.save(bind);
    }
    bind.setLastDate(new Date());

    user = bind.getUser();
    handleUserInfo(result, user);

    return result;
  }

  @Override
  public UserResponse bindByCode(UserLoginCodeRequest request) {
    UserResponse result = new UserResponse();


    if (checkCode(request.getPhone(), request.getCode(), 3, result)) {
      return result;
    }
    Long uid = tokenService.user(request.getUserToken());

    UserInfo user = userInfoDao.findById(uid);
    if (user == null) {
      result.setCode(501);
      result.setMsg("没有用户信息");
      return result;
    }

    UserBind bind = bindDao.findByName(request.getPhone());
    if (bind == null) {
      user.setPhone(request.getPhone());
      bind = new UserBind();
      bind.setBindType(BindType.phone);
      bind.setNo(request.getPhone());
      bind.setUser(user);
      bindDao.save(bind);
    }
    bind.setLastDate(new Date());

    handleUserInfo(result, user);

    return result;
  }

  @Override
  public UserResponse registerByCode(UserRegisterCodeRequest request) {
    UserResponse result = new UserResponse();

    if (request.getPhone() == null) {
      result.setCode(-1);
      result.setMsg("手机号不能为空");
      return result;
    }
    if (checkCode(request.getPhone(), request.getCode(), 1, result)) {
      return result;
    }

    UserInfo user = null;
    UserBind userBind = bindDao.findByPhone(request.getPhone());
    if (userBind != null) {
      result.setCode(-5);
      result.setMsg("改手机号已经注册过了");
      return result;
    }
    user = new UserInfo();
    user.setName(name(request.getPhone()));
    user.setLoginSize(0);
    user.setLastDate(new Date());
    user.setAddDate(new Date());
    user.setAvatar("");
    user.setPhone(request.getPhone());
    userInfoDao.save(user);


    userBind = new UserBind();
    userBind.setBindType(BindType.phone);
    userBind.setUser(user);
    userBind.setNo(request.getPhone());
    bindDao.save(userBind);

    UserSecurity security = new UserSecurity();
    security.setCheckSize(0);
    security.setSecurityType(SecurityType.account);
    SecurityUtil util = new SecurityUtil();
    security.setSalt(util.getSalt());
    security.setPassword(util.entryptPassword(request.getPassword()));
    security.setUser(user);
    securityDao.save(security);
    handleUserInfo(result, user);

    return result;
  }

  public String name(String phone) {
    if (phone == null) {
      return "用户";
    }
    if (phone.length() > 10) {
      return phone.substring(0, 3) + "****" + phone.substring(7);
    } else {
      return phone;
    }

  }

  public static void main(String[] args) {
    UserResource resource = new UserResource();
    System.out.println(resource.name("18229060103"));
  }

  private void handleUserInfo(UserResponse result, UserInfo user) {
    if (user == null) {
      result.setCode(-2);
      result.setMsg("你还没有绑定用户");
      return;
    }
    result.setUserToken(tokenService.token(user.getId()));
    if (StringUtils.hasText(user.getName())){
      String name = EmojiParser.parseToUnicode(user.getName());
      result.setName(name);
    }else{
      result.setName("用户");
    }
    result.setAvatar(user.getAvatar());
    result.setPhone(user.getPhone());
    result.setId(user.getId());
    UserBind bind = bindDao.findByUser(user.getId(), BindType.phone);
    if (bind == null) {
      result.setHavePhone(false);
      result.setPhone("");
    } else {
      result.setPhone(bind.getNo());
      result.setHavePhone(true);
    }

  }

  @Override
  public UserResponse login(UserLoginRequest request) {
    UserResponse result = new UserResponse();
    UserBind bind = bindDao.findByName(request.getNo());
    UserInfo user = null;
    if (bind == null) {
      UserAccount account = accountDao.findByName(request.getNo());
      if (account == null) {
        result.setCode(501);
        result.setMsg("该账号不存在!");
        return result;
      }
      SecurityUtil util = new SecurityUtil(account.getSalt());
      if (!util.checkPassword(account.getPassword(), request.getPassword())) {
        result.setCode(502);
        result.setMsg("密码错误!");
        return result;
      }
      user = account.getUser();
    }
    if (user == null) {
      user = bind.getUser();
      UserSecurity security = securityDao.findByUser(user.getId(), SecurityType.account);
      if(security==null){
        result.setCode(503);
        result.setMsg("你没有设置密码!");
        return result;
      }
      SecurityUtil util = new SecurityUtil(security.getSalt());
      if (!util.checkPassword(security.getPassword(), request.getPassword())) {
        result.setCode(503);
        result.setMsg("密码错误!");
        return result;
      }
    }
    handleUserInfo(result, user);
    return result;
  }

  @Override
  public UserResponse loginOauth(UserLoginOatuthRequest request) {
    UserResponse result = new UserResponse();

    Handler handler = (handlerRequest, handlerResponse) -> {
      TokenResponse response = (TokenResponse) handlerRequest.getAttribute("response");
      UserOauthToken token = oauthTokenDao.findByOpenId(response.getOpenId(), request.getType());
      if (token != null) {
        Integer size = token.getLoginSize();
        if (size == null) {
          size = 0;
        }
        size++;
        token.setLoginSize(size);
        token.setAccess_token(response.getAccessToken());
        token.setRefresh_token(response.getRefreshToken());
        token.setLastDate(new Date());
        handleUserInfo(result, token.getUser());
      } else {
        if ("none".equals(request.getStrategy())) {
          result.setCode(-2);
          result.setMsg("没有注册，请注册！");
        } else {
          OauthResponse oauthResponse = (OauthResponse) handlerRequest.getAttribute("oauthResponse");
          UserInfo userInfo = new UserInfo();
          if (oauthResponse != null) {
            String name = EmojiParser.parseToAliases(oauthResponse.getName());
            userInfo.setName(name);
            userInfo.setAvatar(oauthResponse.getAvatar());
          }
          userInfoDao.save(userInfo);
          token = new UserOauthToken();
          token.setUser(userInfo);
          token.setLoginSize(0);
          token.setUid(response.getOpenId());
          token.setToken_type(request.getType());
          token.setAccess_token(response.getAccessToken());
          oauthTokenDao.save(token);
          handleUserInfo(result, token.getUser());
        }

      }
      //保存统一账号信息
      if (StringUtils.hasText(response.getUnionid())) {
        UserOauthToken commontoken = oauthTokenDao.findByOpenId(response.getUnionid(), "weicommon");
        if (commontoken == null && token != null && token.getUser() != null) {
          commontoken = new UserOauthToken();
          commontoken.setToken_type("weicommon");
          commontoken.setUid(response.getUnionid());
          commontoken.setUser(token.getUser());
          commontoken.setAccess_token(response.getAccessToken());
          commontoken.setRefresh_token(response.getRefreshToken());
          commontoken.setLoginSize(0);
          oauthTokenDao.save(commontoken);
        }
      }
    };
    handleOauthWork(request, new ResponseAdapter(result), handler);


    return result;
  }

  @Override
  public UserResponse findOpenId(UserLoginOatuthRequest request) {
    UserResponse result=new UserResponse();
    OauthHandler oauthHandler = configDao.id(request.getType());
    if (oauthHandler == null) {
      result.setCode(501);
      result.setMsg("该登陆方式无效");
      return result;
    }
    TokenResponse response = oauthHandler.getToken(request.getCode());
    if (response != null) {
      result.setOpenId( response.getOpenId());
    }
    return result;
  }


  @Override
  public UserResponse loginOauthOk(UserLoginOatuthRequest request) {
    request.setStrategy("create");
    return loginOauth(request);
  }


  @Override
  public UserResponse registerOauth(UserRegisterOatuthRequest request) {


    UserResponse result = new UserResponse();


    if (checkCode(request.getPhone(), request.getCode(), 3, result)) {
      return result;
    }


    UserInfo user = new UserInfo();

    String openid = "";

    OauthHandler oauthHandler = configDao.id(request.getType());
    if (oauthHandler == null) {
      result.setCode(-1);
      result.setMsg("该登陆方式无效");
      return result;
    }
    OauthResponse response = oauthHandler.login(request.getAccessToken(), request.getOpenId());

    if (response != null) {
      openid = response.getOpenid();
      user.setName(response.getName());
      user.setAvatar(response.getAvatar());
    }
    if (openid == null || openid.length() == 0) {
      result.setCode(-3);
      result.setMsg("链接第三方失败");
      return result;
    }

    UserBind userBind = bindDao.findByPhone(request.getPhone());
    if (userBind == null) {
      userInfoDao.save(user);
      user.setPhone(request.getPhone());
      userBind = new UserBind();
      userBind.setUser(user);
      userBind.setNo(request.getPhone());
      userBind.setBindType(BindType.phone);
      bindDao.save(userBind);
    } else {
      UserInfo dbuser = userBind.getUser();
      if (StringUtils.isEmpty(dbuser.getAvatar())) {
        dbuser.setAvatar(user.getAvatar());
      }
      if (StringUtils.isEmpty(dbuser.getName())) {
        dbuser.setName(user.getName());
      }
      user = dbuser;
    }

    UserOauthToken token = oauthTokenDao.findByOpenId(openid, request.getType());
    if (token == null) {
      UserOauthToken oauthToken = new UserOauthToken();
      oauthToken.setLoginSize(0);
      oauthToken.setUser(user);
      oauthToken.setUid(openid);
      oauthToken.setToken_type(request.getType());
      oauthTokenDao.save(oauthToken);
    } else {
      user = token.getUser();
    }

    handleUserInfo(result, user);
    return result;
  }

  @Override
  public UserResponse bindOauth(UserBindOauthRequest request) {
    UserResponse result = new UserResponse();
    Long user = tokenService.user(request.getUserToken());
    if (user == null) {
      result.setMsg("无效令牌");
      result.setCode(501);
      return result;
    }
    OauthHandler oauthHandler = configDao.id(request.getType());
    if (oauthHandler == null) {
      result.setCode(-1);
      result.setMsg("该登陆方式无效");
      return result;
    }
    TokenResponse response = oauthHandler.getToken(request.getCode());

    if (response == null) {
      result.setCode(-3);
      result.setMsg("链接第三方失败");
      return result;
    }
    if (!StringUtils.hasText(response.getOpenId())) {
      result.setCode(-3);
      result.setMsg("链接第三方失败");
      return result;
    }
    UserOauthToken token = oauthTokenDao.findByUser(user, request.getType());
    if (token == null) {
      token = new UserOauthToken();
      token.setUser(UserInfo.fromId(user));
      token.setToken_type(request.getType());
      token.setUid(response.getOpenId());
      token.setAccess_token(response.getAccessToken());
      token.setRefresh_token(response.getRefreshToken());
      oauthTokenDao.save(token);
    }

    return result;
  }

  @Override
  public UserResponse bindPhone(UserBindPhoneRequest request) {
    UserResponse result = new UserResponse();

    if (checkCode(request.getPhone(), request.getCode(), 3, result)) {
      return result;
    }
    Long uid = tokenService.user(request.getUserToken());
    UserInfo user = userInfoDao.findById(uid);
    if (user == null) {
      result.setCode(501);
      result.setMsg("无效令牌");
      return result;
    }

    UserBind bind = bindDao.findByPhone(request.getPhone());
    if (bind == null) {
      user.setPhone(request.getPhone());
      bind = new UserBind();
      bind.setBindType(BindType.phone);
      bind.setNo(request.getPhone());
      bind.setUser(user);
      bindDao.save(bind);
    } else {
      UserInfo temp = bind.getUser();
      if (temp != null) {
        if (StringUtils.isEmpty(temp.getName())){
          temp.setName(user.getName());
        }
        if (StringUtils.isEmpty(temp.getAvatar())){
          temp.setAvatar(user.getAvatar());
        }
        user.setStoreState(StoreState.archive);
        List<UserOauthToken> ts = oauthTokenDao.tokens(uid);
        if (ts != null) {
          for (UserOauthToken token : ts) {
            token.setUser(temp);
          }
        }
        user=temp;
      }

    }
    handleUserInfo(result, user);

    return result;
  }

  @Override
  public UserResponse resetPassword(UserResetPasswordRequest request) {

    UserResponse result = new UserResponse();

    if (checkCode(request.getPhone(), request.getCode(), 4, result)) {
      return result;
    }

    UserBind userAccount = bindDao.findByPhone(request.getPhone());
    if (userAccount == null) {
      result.setMsg("该账号不存在");
      result.setCode(-5);
      return result;
    }
    SecurityUtil util = new SecurityUtil();
    UserSecurity security = securityDao.findByUser(userAccount.getUser().getId(), SecurityType.account);
    if (security == null) {
      security = new UserSecurity();
      securityDao.save(security);
    }
    security.setSalt(util.getSalt());
    security.setPassword(util.entryptPassword(request.getPassword()));
    handleUserInfo(result, userAccount.getUser());

    return result;
  }

  @Override
  public ResponseObject changePhone(UserChangePhoneRequest request) {

    ResponseObject result = new ResponseObject();
    if (checkCode(request.getPhone(), request.getCode(), 5, result)) {
      return result;
    }
    UserBind userAccount = bindDao.findByPhone(request.getPhone());
    if (userAccount != null) {
      result.setMsg("该手机号已被使用");
      result.setCode(-5);
      return result;
    }
    Handler handler = (handlerRequest, response) -> {
      UserInfo user = (UserInfo) handlerRequest.getAttribute("user");
      UserBind tempAccount = bindDao.findByUser(user.getId(), BindType.phone);
      if (tempAccount == null) {
        tempAccount = new UserBind();
        tempAccount.setNo(request.getPhone());
        tempAccount.setUser(user);
        tempAccount.setBindType(BindType.phone);
        bindDao.save(tempAccount);
      }
      tempAccount.setNo(request.getPhone());
      user.setPhone(request.getPhone());
    };
    handleUserTokenWork(request, new ResponseAdapter(result), handler);
    return result;
  }

  @Override
  public boolean checkCode(String phone, String code, Integer type, ResponseObject result) {
    if (code == null) {
      result.setCode(-1);
      result.setMsg("验证码不能为空");
      return true;
    }
    if (phone == null) {
      result.setCode(-1);
      result.setMsg("手机号不能为空");
      return true;
    }
    UserVerification verification = verificationDao.findByName(phone, type);
    if (verification == null) {
      result.setCode(-2);
      result.setMsg("该验证码不可用");
      return true;
    }
    Date lastDate = verification.getLastDate();
    SendCodeVo validateCode = check(lastDate);
    if (validateCode.getCode() != 0) {
      result.setCode(-3);
      result.setMsg("验证码已经过期");
      return true;
    }
    if (!code.equals(verification.getCode())) {
      result.setCode(-4);
      result.setMsg("验证码错误");
      return true;
    }
    return false;
  }

  @Override
  public ResponseObject checkPhoneCode(CheckPhoneCodeRequest request) {
    ResponseObject result = new ResponseObject();

    Integer catalog = CodeCatalog.catalog(request.getType());
    if (catalog == null) {
      result.setCode(-1);
      result.setMsg("参数异常");
      return result;
    }
    checkCode(request.getPhone(), request.getCode(), catalog, result);
    return result;
  }

  @Override
  public ResponseObject changePassword(UserChangePasswordRequest request) {
    ResponseObject result = new ResponseObject();

    Handler handler = (handlerRequest, response) -> {
      UserInfo user = (UserInfo) handlerRequest.getAttribute("user");
      UpdatePasswordRequest updateRequest = new UpdatePasswordRequest();
      updateRequest.setSecurityType(SecurityType.account);
      updateRequest.setPassword(request.getPassword());
      updateRequest.setOldPassword(request.getOldPassword());
      updateRequest.setId(user.getId());
      ResponseObject temp = userInfoDao.updatePassword(updateRequest);
      result.setMsg(temp.getMsg());
      result.setCode(temp.getCode());
    };
    handleUserTokenWork(request, new ResponseAdapter(result), handler);

    return result;
  }

  private void handleUserTokenWork(UserTokenRequest request, HandlerResponse response, Handler handler) {
    RestRequest handleRequest = new RestRequest();
    handleRequest.putParameter("userToken", request.getUserToken());
    handles(handleRequest, response, handler, this.context.getBean(CheckUserTokenFilter.class));
  }

  private void handleOauthWork(UserLoginOatuthRequest request, HandlerResponse response, Handler handler) {
    RestRequest handleRequest = new RestRequest();
    handleRequest.putParameter("code", request.getCode());
    handleRequest.putParameter("type", request.getType());

    handles(handleRequest, response, handler, this.context.getBean(ExtractOauthFilter.class));
  }

  private void handles(RestRequest handleRequest, HandlerResponse response, Handler handler, Filter... filters) {
    MockFilterChain chain = new MockFilterChain();
    if (filters != null && filters.length > 0) {
      for (Filter filter : filters) {
        chain.addFilter(filter);
      }
    }
    chain.addFilter(new HandlerFilterProxy(handler));
    chain.doFilter(handleRequest, response);
  }

  @Override
  public UserResponse update(UserUpdateRequest request) {
    UserResponse result = new UserResponse();
    Handler handler = (handlerRequest, response) -> {
      UserInfo user = (UserInfo) handlerRequest.getAttribute("user");
      if (StringUtils.hasText(request.getName())) {
        user.setName(request.getName());
      }
      if (StringUtils.hasText(request.getAvatar())) {
        user.setAvatar(request.getAvatar());
      }
      handleUserInfo(result, user);
    };
    handleUserTokenWork(request, new ResponseAdapter(result), handler);
    return result;
  }

  @Override
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    this.context = applicationContext;
  }
}
